/*
 * The link: http://poj.org/problem?id=1088
 * My original thoughts is to use Dijkstra algorithm, but POJ gives me TLE, because of the data structure
 * I used is array, not heap.
 * Then I check the Discuss of this problem, finding the Dynamic Programming implementation of this problem.
 * When I first look at this problem, also I have thought the DP way. But I couldn't get the state equations.
 * The below implementation is the same as the implementation of the thread in the Discuss.
 * The commented part is my implementation of Dijkstrat algorithm.
 * I originally stuck at when to stop the iteration process. The memory function solves this doubt.
 */

#include <iostream>

using namespace std;

const int MAX_NUMBER_OF_POINTS = 100 * 100;
const int MAX_LENGTH = 100;
int numberOfRows, numberOfColumns;
// The matrix of the district
int district[MAX_LENGTH][MAX_LENGTH];
// The matrix of each vertex's path length
int path[MAX_LENGTH][MAX_LENGTH];

/* 
 * This function is not used any more
 * Deprecated
 */
int deletemin(int* lengthFromStart, int row, int column, int &minLen) 
{
	minLen = MAX_NUMBER_OF_POINTS + 1;
	int minIndex;
	for (int i = 0; i < row; i++) {
		for (int j = 0; j < column; j++) {
			int index = i * column + j;
			if(lengthFromStart[index] < minLen) {
				minLen = lengthFromStart[index];
				minIndex = index;
			}
		}
	}
	if (minLen != MAX_NUMBER_OF_POINTS + 1) {
		lengthFromStart[minIndex] = MAX_NUMBER_OF_POINTS + 1;
	} else {
		minIndex = MAX_NUMBER_OF_POINTS + 1;
	}
	return minIndex;
}

/*
 * @param i: The row index, j: The column index
 */
int calculateHeight(int i, int j)
{
	if (path[i][j] > 0) {
		return path[i][j];
	}
	int max = 0, temp;
	if (i > 0) {
		if (district[i][j] > district[i - 1][j]) {
			temp = calculateHeight(i - 1, j);
			if (max < temp)
				max = temp;
		}
	}

	if (j > 0) {
		if (district[i][j] > district[i][j - 1]) {
			temp = calculateHeight(i, j - 1);
			if (max < temp)
				max = temp;
		}
	}

	if (i < numberOfRows - 1) {
		if (district[i][j] > district[i + 1][j]) {
			temp = calculateHeight(i + 1, j);
			if (max < temp)
				max = temp;
		}
	}

	if (j < numberOfColumns - 1) {
		if (district[i][j] > district[i][j + 1]) {
			temp = calculateHeight(i, j + 1);
			if (max < temp)
				max = temp;
		}
	}
	return max + 1;
}

int main(int argc, char *argv[])
{
	
	// Read the input
	cin >> numberOfRows >> numberOfColumns;
	for (int i = 0; i < numberOfRows; i++) {
		for (int j = 0; j < numberOfColumns; j++) {
			cin >> district[i][j];
		}
	}

	// Initializing
	for (int i = 0; i < numberOfRows; i++) {
		for (int j = 0; j < numberOfColumns; j++) {
			path[i][j] = 0;
		}
	}

	// Calculating
	for (int i = 0; i < numberOfRows; i++) {
		for (int j = 0; j < numberOfColumns; j++) {
			path[i][j] = calculateHeight(i, j);
		}
	}

	// Find the maximum
	for (int i = 0; i < numberOfRows; i++) {
		for (int j = 0; j < numberOfColumns; j++) {
			if (path[0][0] < path[i][j])
				path[0][0] = path[i][j];
		}
	}

	cout << path[0][0] << endl;

	//int pathLength[MAX_NUMBER_OF_POINTS];
	//memset(pathLength, 0, sizeof(pathLength));

	//// The starting point of searching: the height of the neighboring points are all lower than the alternative point
	//int alternatives[MAX_NUMBER_OF_POINTS];
	//// The index
	//int idx = 0;
	//for (int i = 0; i < numberOfRows; i++) {
	//	for (int j = 0; j < numberOfColumns; j++) {			
	//		int currentIndex = i * numberOfColumns + j;
	//		int tempRowIndex = i > 0 ? i - 1 : i;
	//		int tempColumnIndex = j > 0 ? j - 1 : j;
	//		// The upper point's index
	//		int tempIndex = tempRowIndex * numberOfColumns + j;
	//		if (district[currentIndex] < district[tempIndex]) {
	//			continue;
	//		}
	//		// The left point's index
	//		tempIndex = i * numberOfColumns + tempColumnIndex;
	//		if (district[currentIndex] < district[tempIndex]) {
	//			continue;
	//		}

	//		tempRowIndex = i < numberOfRows - 1 ? i + 1 : numberOfRows - 1;
	//		tempColumnIndex = j < numberOfColumns - 1 ? j + 1 : numberOfColumns - 1;
	//		// The right point's index
	//		tempIndex = i * numberOfColumns + tempColumnIndex;
	//		if (district[currentIndex] < district[tempIndex]) {
	//			continue;
	//		}

	//		// The lower point's index
	//		tempIndex = tempRowIndex * numberOfColumns + j;
	//		if (district[currentIndex] < district[tempIndex]) {
	//			continue;
	//		}
	//		alternatives[idx++] = currentIndex;
	//	}
	//}
	//
	//int minimumNegativePathLength = 0;
	//for (int i = 0; i < idx; i++) {
	//	int lengthFromStart[MAX_NUMBER_OF_POINTS];
	//	
	//	for (int j = 0; j < MAX_NUMBER_OF_POINTS; j++) {
	//		lengthFromStart[j] = MAX_NUMBER_OF_POINTS + 1;
	//	}

	//	lengthFromStart[alternatives[i]] = 0;
	//	int tempMinimumPathLength = MAX_NUMBER_OF_POINTS + 1;
	//	// Assume the edge length to be -1
	//	while (true) {
	//		int minLen;
	//		int minIndex = deletemin(lengthFromStart, numberOfRows, numberOfColumns, minLen);
	//		if (minIndex == MAX_NUMBER_OF_POINTS + 1)
	//			break;
	//		int rowIndex = minIndex / numberOfColumns;
	//		int columnIndex = minIndex % numberOfColumns;
	//		int tempIndex;
	//		// The upper point
	//		if (rowIndex > 0) {
	//			tempIndex = (rowIndex - 1) * numberOfColumns + columnIndex;
	//			if (district[minIndex] > district[tempIndex]) {
	//				if (lengthFromStart[tempIndex] > minLen - 1) {
	//					lengthFromStart[tempIndex] = minLen - 1;
	//					if (tempMinimumPathLength > lengthFromStart[tempIndex])
	//						tempMinimumPathLength = lengthFromStart[tempIndex];
	//				}
	//			}
	//		}

	//		// The left point
	//		if (columnIndex > 0) {
	//			tempIndex = rowIndex * numberOfColumns + columnIndex - 1;
	//			if (district[minIndex] > district[tempIndex]) {
	//				if (lengthFromStart[tempIndex] > minLen - 1) {
	//					lengthFromStart[tempIndex] = minLen - 1;
	//					if (tempMinimumPathLength > lengthFromStart[tempIndex])
	//						tempMinimumPathLength = lengthFromStart[tempIndex];
	//				}
	//			}
	//		}

	//		// The right point
	//		if (columnIndex < numberOfColumns - 1) {
	//			tempIndex = rowIndex * numberOfColumns + columnIndex + 1;
	//			if (district[minIndex] > district[tempIndex]) {
	//				if (lengthFromStart[tempIndex] > minLen - 1) {
	//					lengthFromStart[tempIndex] = minLen - 1;
	//					if (tempMinimumPathLength > lengthFromStart[tempIndex])
	//						tempMinimumPathLength = lengthFromStart[tempIndex];
	//				}
	//			}
	//		}

	//		// The lower point
	//		if (rowIndex < numberOfRows - 1) {
	//			tempIndex = (rowIndex + 1) * numberOfColumns + columnIndex;
	//			if (district[minIndex] > district[tempIndex]) {
	//				if (lengthFromStart[tempIndex] > minLen - 1) {
	//					lengthFromStart[tempIndex] = minLen - 1;
	//					if (tempMinimumPathLength > lengthFromStart[tempIndex])
	//						tempMinimumPathLength = lengthFromStart[tempIndex];
	//				}
	//			}
	//		}
	//	} // end while true
	//	if (minimumNegativePathLength > tempMinimumPathLength) {
	//		minimumNegativePathLength = tempMinimumPathLength;
	//	}		
	//}
	//cout << minimumNegativePathLength * (-1) + 1 << endl;

	return 0;
}